package com.wcpdoc.exam.core.service.impl;

import com.wcpdoc.base.cache.ProgressBarCache;
import com.wcpdoc.base.dao.DictDao;
import com.wcpdoc.base.em.LogType;
import com.wcpdoc.base.entity.Dict;
import com.wcpdoc.base.service.OpLogService;
import com.wcpdoc.core.dao.BaseDao;
import com.wcpdoc.core.entity.PageResult;
import com.wcpdoc.core.exception.MyException;
import com.wcpdoc.core.service.impl.BaseServiceImp;
import com.wcpdoc.core.util.BigDecimalUtil;
import com.wcpdoc.core.util.DateUtil;
import com.wcpdoc.core.util.StringUtil;
import com.wcpdoc.core.util.ValidateUtil;
import com.wcpdoc.exam.core.cache.QuestionCache;
import com.wcpdoc.exam.core.dao.MyExamMockDao;
import com.wcpdoc.exam.core.entity.*;
import com.wcpdoc.exam.core.entity.ex.ExamMockInfo;
import com.wcpdoc.exam.core.service.MyExamMockService;
import com.wcpdoc.exam.core.service.MyMockQuestionService;
import com.wcpdoc.exam.core.service.QuestionService;
import com.wcpdoc.exam.core.util.QuestionUtil;
import com.wcpdoc.file.service.FileService;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 我的模拟考试服务层实现
 * 
 */
@Service
public class MyExamMockServiceImpl extends BaseServiceImp<MyExamMock> implements MyExamMockService {
	private static final Logger log = LoggerFactory.getLogger(MyExamMockServiceImpl.class);

	@Resource
	private OpLogService opLogService;
	@Resource
	private MyExamMockDao myExamMockDao;
	@Resource
	private DictDao dictDao;
	@Resource
	private QuestionService questionService;
	@Resource
	private MyMockQuestionService myMockQuestionService;
	@Resource
	private MyExamMockService myExamMockService;

	@Override
	@Resource(name = "myExamMockDaoImpl")
	public void setDao(BaseDao<MyExamMock> dao) {
		super.dao = dao;
	}

	@Override
	public List<MyExamMock> getList(Integer examId) {
		return myExamMockDao.getList(examId);
	}
	
	@Override
	public List<MyExamMock> getListForUser(Integer userId) {
		return myExamMockDao.getListForUser(userId);
	}
	
	@Override
	public MyExamMock getMyExamMock(Integer examId, Integer userId) {
		return myExamMockDao.getMyExamMock(examId, userId);
	}

	@Override
	public void answerUpdate(Integer examId, Integer userId, Integer questionId, String[] answers) {
		// 校验数据有效性
		if (examId == null) {
			throw new MyException("参数错误：examId");
		}
		if (userId == null) {
			throw new MyException("参数错误：userId");
		}
		if (questionId == null) {
			throw new MyException("参数错误：questionId");
		}

		// if(!ValidateUtil.isValid(answer)) {
		// throw new MyException("参数错误：answer");
		// }//如是多选取消勾选则为空

		MyMockQuestion myQuestion = myMockQuestionService.getMyMockQuestion(examId, userId, questionId);
		if (myQuestion == null) {
			throw new MyException("未参与考试");
		}
		MyExamMock myExam = getMyExamMock(examId, userId);
//		Exam exam = examService.getEntity(examId);
		if (myExam.getState() == 0) {
			throw new MyException("考试已删除");
		}
//		if (myExam.getState() == 2) {
//			throw new MyException("考试已暂停");
//		}
		long curTime = System.currentTimeMillis();
//		if (myExam.getStartTime().getTime() > curTime) {
//			throw new MyException("考试未开始");
//		}
//		if (curTime - myExam.getAnswerEndTime().getTime() > 1000) {// 预留1秒网络延时
//			throw new MyException("考试已结束");
//		}
//		MyExamMock myExam = getMyExamMock(examId, userId);
		if (myExam.getState() == 3) {
			throw new MyException("已交卷");
		}
		if(myExam.getAnswerStartTime()==null){
			throw new MyException("未开始考试");
		}

		// 标记为考试中
		myExam.setState(2);
//		if (!ValidateUtil.isValid(myExam.getAnswerStartTime())) {
//			myExam.setAnswerStartTime(new Date());
////			myExam.setAnswerEndTime(new Date());// 如果只答一道题，这里不加，结束时间就是空
//		} else {
//			myExam.setAnswerEndTime(new Date());
//		}
		myExam.setAnswerEndTime(new Date());
		//如果考试时间超过开始考试时间+考试时长，则提示考试已结束
//		System.out.println(DateUtil.getNextMinute(myExam.getAnswerStartTime(),exam.getPassTime()).getTime());
//		System.out.println(new Date().getTime());
		if (myExam.getAnswerStartTime()!=null&&
				DateUtil.getNextMinute(myExam.getAnswerStartTime(),myExam.getPassTime()).getTime()<=new Date().getTime()) {
			throw new MyException("考试已结束");
		}

		update(myExam);
		
		// 保存答案
		Question question = QuestionCache.getQuestion(questionId);// 已关联考试的试题不会改变，缓存起来加速查询。
		if (!ValidateUtil.isValid(answers)) {
			myQuestion.setUserAnswer(null);
		} else if (QuestionUtil.hasTrueFalse(question)) {
			myQuestion.setUserAnswer(answers[0]);
		} else if (QuestionUtil.hasSingleChoice(question)) {
			if (ValidateUtil.isValid(myQuestion.getOptionsNo())) {
				myQuestion.setUserAnswer(myQuestion.getOptionsNoCacheOfAnswer().get(answers[0]));
			} else {
				myQuestion.setUserAnswer(answers[0]);
			}
		} else if (QuestionUtil.hasMultipleChoice(question)) {
			if (ValidateUtil.isValid(myQuestion.getOptionsNo())) {
				for (int i = 0; i < answers.length; i++) {
					answers[i] = myQuestion.getOptionsNoCacheOfAnswer().get(answers[i]);
				}
			}
			
			Arrays.sort(answers);// 页面先选d在选c，值为db，这里重新排序一下
			myQuestion.setUserAnswer(StringUtil.join(answers));
		} else if (QuestionUtil.hasFillBlank(question)) {
			myQuestion.setUserAnswer(StringUtil.join(answers, '\n'));
		} else if (QuestionUtil.hasQA(question)) {
			myQuestion.setUserAnswer(StringUtil.join(answers));// bug：文本包含英文逗号会分割
		}
		myQuestion.setAnswerTime(new Date());
		myMockQuestionService.update(myQuestion);
	}

	@Override
	public void finish(Integer examId, Integer userId) {
		// 校验数据有效性
		if (!ValidateUtil.isValid(examId)) {
			throw new MyException("参数错误：examId");
		}
		if (!ValidateUtil.isValid(userId)) {
			throw new MyException("参数错误：userId");
		}
		MyExamMock myExam = getMyExamMock(examId, userId);
		if (myExam == null) {
			throw new MyException("未参与考试");
		}
		if (myExam.getState() == 3) {
			throw new MyException("考试已交卷");
		}
//		Exam exam = examService.getEntity(examId);
		if (myExam.getState() == 0) {
			throw new MyException("考试已删除");
		}
//		if (myExam.getState() == 2) {
//			throw new MyException("考试已暂停");
//		}
//		long curTime = System.currentTimeMillis();
//		if (myExam.getStartTime().getTime() > curTime) {
//			throw new MyException("考试未开始");
//		}
//		if (curTime - myExam.getEndTime().getTime() > 1000) {// 预留1秒网络延时
//			throw new MyException("考试已结束");
//		}
		
		// 标记用户为已交卷
		myExam.setState(3);
//		if (exam.getMarkType() == 1) {// 如果是客观题试卷，记录阅卷开始时间
//			myExam.setMarkStartTime(new Date());// 主观题阅卷开始时间，由阅卷用户第一次打分时生成
//		}
		if (myExam.getAnswerStartTime() == null) {// 一道题也没答，直接交卷
			myExam.setAnswerStartTime(new Date());
		}
		myExam.setAnswerEndTime(new Date());// 最后交卷时间就是答题结束时间
		
		// 批阅客观题
		List<MyMockQuestion> myQuestionList = myMockQuestionService.getList(examId, userId);
		BigDecimalUtil objectiveScore = BigDecimalUtil.newInstance(0);// 客观题分数
		for (MyMockQuestion myQuestion : myQuestionList) {
			if (myQuestion.getType() == 1) {// 如果是章节，不处理
				continue;
			}
			Question question = QuestionCache.getQuestion(myQuestion.getQuestionId());// 已关联考试的试题不会改变，缓存起来加速查询。
			if (QuestionUtil.hasSubjective(question)) {// 如果是主观题，等待人工阅卷
				continue;
			}
			
			List<QuestionAnswer> questionAnswerList = QuestionCache.getAnswer(question.getId());
//			if (QuestionUtil.hasQA(question)) {
//				qAHandle(question, questionAnswerList, myQuestion);// 问答处理
//			} else
			if (QuestionUtil.hasSingleChoice(question) || QuestionUtil.hasTrueFalse(question)) {
				singleChoiceHandle(question, questionAnswerList, myQuestion);// 单选判断处理
			} else if (QuestionUtil.hasMultipleChoice(question)) { 
				multipleChoiceHandle(question, questionAnswerList, myQuestion);// 多选处理
			}
//			else if (QuestionUtil.hasFillBlank(question)) {
//				fillBlankHandle(question, questionAnswerList, myQuestion);// 填空处理
//			}
			
//			myQuestion.setMarkTime(new Date());
//			myQuestion.setMarkUserId(1);
			myMockQuestionService.update(myQuestion);// 更新每道题的分数（没作答也都标记为0分。影响的地方为人工阅卷时，所有题都有分数，才允许阅卷完成。）
			
			objectiveScore.add(myQuestion.getUserScore());// 累加客观题分数
		}
		
		// 如果是客观题试卷，直接出成绩
//		if (exam.getMarkType() == 1) {
//			myExam.setMarkUserId(1);// 阅卷人为admin
//			myExam.setMarkEndTime(new Date());// 记录阅卷结束时间（阅卷开始时间在上面，中间是阅题时间，客观题试卷有效。主观题时间由阅卷用户打分时生成）
			myExam.setAnswerEndTime(new Date());
			myExam.setMarkState(3);// 标记为阅卷结束
			myExam.setObjectiveScore(objectiveScore.getResult());
			myExam.setTotalScore(objectiveScore.getResult());// 客观题分数就是总分数
			myExam.setAnswerState(BigDecimalUtil.newInstance(myExam.getTotalScore()).sub(myExam.getPassScore()).getResult().doubleValue() >= 0 ? 1 : 2);// 标记用户是否及格
			update(myExam);
//		}
		// 如果是主观题试卷，只标记客观题分数。下一步流程进入人工阅卷
//		else if (exam.getMarkType() == 2) {
//			myExam.setObjectiveScore(objectiveScore.getResult());
//			update(myExam);
//		}
	}

	@Override
	public void doExam(Integer examId){
		doExamEx(examId,false);
	}

	private void doExamEx(Integer examId,Boolean isValid) {
		// 延时2秒后开始（答题时预留了1秒网络延时，这里在延时1秒，保证都是答题完成后的结果）
		MyExamMock exam = myExamMockService.getEntity(examId);
		doExamTimedelay(exam);// 业务上变更考试结束时间为当前时间时，自动阅卷概率性校验为考试未结束（数据库时间四舍五入后比当前时间大。例：传入值：2022-05-12 23:59:59,999999，保存为 2022-05-13 00:00:00），所以先延时后校验
		if(isValid){
			// 校验数据有效性
			doExamValid(exam);
		}
		
		// 获取考试用户列表
		List<MyExamMock> myExamList = myExamMockService.getList(examId);
		
		// 开始阅卷
		for (MyExamMock myExam : myExamList) {
			if(myExam.getState() == 3){// 提前交卷的已经阅卷，不处理
				continue;
			}
			
			if (myExam.getState() == 2) {// 如果未交卷，标记为已交卷
				myExam.setState(3);
			}
			
//			if (exam.getMarkType() == 1 || myExam.getState() == 1) {// 如果是客观题试卷或未考试，记录阅卷开始时间；
//				myExam.setMarkStartTime(new Date());
//			}
			
			List<MyMockQuestion> myQuestionList = myMockQuestionService.getList(examId, myExam.getUserId());
			BigDecimalUtil objectiveScore = BigDecimalUtil.newInstance(0);// 客观题分数
			for (MyMockQuestion myQuestion : myQuestionList) {
				if (myQuestion.getType() == 1) {// 如果是章节，不处理
					continue;
				}
				if (myExam.getState() == 1) {// 如果未考试，标记用户当前题为0分（阅卷时需要所有题都有分数才能进行）
//					myQuestion.setMarkTime(new Date());
//					myQuestion.setMarkUserId(1);
					myQuestion.setUserScore(BigDecimal.ZERO);
					myMockQuestionService.update(myQuestion);
					continue;
				}
				
				Question question = QuestionCache.getQuestion(myQuestion.getQuestionId());// 已关联考试的试题不会改变，缓存起来加速查询。
				if (QuestionUtil.hasSubjective(question)) {// 如果是主观题，等待人工阅卷
					continue;
				}
				
				List<QuestionAnswer> questionAnswerList = QuestionCache.getAnswer(question.getId());
//				if (QuestionUtil.hasQA(question)) {
//					qAHandle(question, questionAnswerList, myQuestion);// 问答处理
//				} else
				if (QuestionUtil.hasSingleChoice(question) || QuestionUtil.hasTrueFalse(question)) {
					singleChoiceHandle(question, questionAnswerList, myQuestion);// 单选判断处理
				} else if (QuestionUtil.hasMultipleChoice(question)) { 
					multipleChoiceHandle(question, questionAnswerList, myQuestion);// 多选处理
				}
//				else if (QuestionUtil.hasFillBlank(question)) {
//					fillBlankHandle(question, questionAnswerList, myQuestion);// 填空处理
//				}
				
//				myQuestion.setMarkTime(new Date());
//				myQuestion.setMarkUserId(1);
				myMockQuestionService.update(myQuestion);// 更新每道题的分数（没作答也都标记为0分。影响的地方为人工阅卷时，所有题都有分数，才允许阅卷完成。）
				
				objectiveScore.add(myQuestion.getUserScore());// 累加客观题分数
			}
			
//			myExam.setMarkUserId(1);// 阅卷人为admin
//			myExam.setMarkEndTime(new Date());// 记录阅卷结束时间（阅卷开始时间在上面，中间是阅题时间，客观题试卷有效。主观题时间由阅卷用户打分时生成）
			myExam.setAnswerEndTime(new Date());
			myExam.setMarkState(3);// 标记为阅卷结束
			myExam.setObjectiveScore(objectiveScore.getResult());
			myExam.setTotalScore(myExam.getObjectiveScore());// 客观题分数就是总分数
			myExam.setAnswerState(BigDecimalUtil.newInstance(myExam.getTotalScore()).sub(exam.getPassScore()).getResult().doubleValue() >= 0 ? 1 : 2);// 标记用户是否及格
			update(myExam);
		}
		
		// 更新用户排名
//		doExamRank(exam, myExamList);
		
		// 完成考试
		doExamFinish(exam);
	}

	private void doExamFinish(MyExamMock exam) {
//		if (exam.getMarkType() == 1) {
//			exam.setMarkState(3);// 标记考试为已阅卷
//			examService.update(exam);
//
//			AutoMarkCache.del(exam.getId());// 不在runner类删除是因为，如果是主观题试卷，缓存需要重新放入一份
//		} else if (exam.getMarkType() == 2) {
//			exam.setMarkState(2);// 标记考试为阅卷中，等待人工阅卷。（人工阅卷校时先校验状态是否变更为2，保证顺序执行）
//			examService.update(exam);
//
//			AutoMarkCache.put(exam.getId(), exam);// 重新放入缓存是因为，考试的阅卷状态已经变更
//		}
		exam.setMarkState(3);// 标记考试为已阅卷
		myExamMockService.update(exam);

//		AutoMarkCache.del(exam.getId());// 不在runner类删除是因为，如果是主观题试卷，缓存需要重新放入一份
	}

	private void doExamTimedelay(MyExamMock exam) {
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			log.error("客观题阅卷异常：【{}-{}】延时执行异常", exam.getId(), exam.getName());
			throw new MyException("延时执行异常");
		}
	}

	private void doExamValid(MyExamMock exam) {
		// 校验数据有效性
		if (exam.getState() == 0) {
			throw new MyException(String.format("【%s-%s】已删除", exam.getId(), exam.getName()));
		}
		if (exam.getState() == 2) {
			throw new MyException(String.format("【%s-%s】已暂停", exam.getId(), exam.getName()));
		}
//		Date curTime = new Date();
//		if (exam.getEndTime().getTime() > curTime.getTime()){
//			throw new MyException(String.format("【%s-%s】考试未结束", exam.getId(), exam.getName()));
//		}
		if (exam.getMarkState() != 1) {// 等于2也表示自动阅卷已完成
			throw new MyException(String.format("【%s-%s】已阅卷", exam.getId(), exam.getName()));
		}
	}
	
	/**
	 * 单选处理
	 * 
	 * @param question 试题
	 * @param questionAnswerList 试题答案
	 * @param myQuestion 我的试题
	 * void
	 */
	private void singleChoiceHandle(Question question, List<QuestionAnswer> questionAnswerList, MyMockQuestion myQuestion) {
		// 校验数据有效性
		if (QuestionUtil.hasSubjective(question)) {
			return;
		}
		
		// 如果用户没有做答，0分
		if (!ValidateUtil.isValid(myQuestion.getUserAnswer())) {
			myQuestion.setUserScore(BigDecimal.ZERO);
			return;
		}
		
		// 阅题
		myQuestion.setUserScore(BigDecimal.ZERO);// 先初始化用户分数为0，防止多次累加
		if (questionAnswerList.get(0).getAnswer().equals(myQuestion.getUserAnswer())) {
			myQuestion.setUserScore(myQuestion.getScore());
		}
	}
	
	/**
	 * 多选处理
	 * 
	 * @param question 试题
	 * @param questionAnswerList 试题答案
	 * @param myQuestion 我的试题
	 * void
	 */
	private void multipleChoiceHandle(Question question, List<QuestionAnswer> questionAnswerList, MyMockQuestion myQuestion) {
		// 校验数据有效性
		if (QuestionUtil.hasSubjective(question)) {
			return;
		}
		
		// 如果用户没有做答，0分
		if (!ValidateUtil.isValid(myQuestion.getUserAnswer())) {
			myQuestion.setUserScore(BigDecimal.ZERO);
			return;
		}
		
		// 阅题
		myQuestion.setUserScore(BigDecimal.ZERO);// 先初始化用户分数为0，防止多次累加
		Set<String> userAnswers = new HashSet<String>(Arrays.asList(myQuestion.getUserAnswer().split(",")));// 获取用户答案
		Set<String> questionAnswers = new HashSet<>(Arrays.asList(questionAnswerList.get(0).getAnswer().split(",")));// 获取试题答案
		if (questionAnswers.size() == userAnswers.size() && questionAnswers.containsAll(userAnswers)) {// 如果完全正确，得满分
			myQuestion.setUserScore(myQuestion.getScore());
		} else if (questionAnswers.containsAll(userAnswers)) {// 如果半对，得漏选分
			myQuestion.setUserScore(myQuestion.getScores()[0]);
		}
	}

	@Override
	public void clear(Integer examId) {
		myExamMockDao.clear(examId);
	}

	/**
	 * 开始考试
	 *
	 * @param examId
	 * @param userId
	 */
	@Override
	public void startExam(Integer examId, Integer userId) {
		MyExamMock myExam = getMyExamMock(examId, userId);
		if (myExam.getState() == 3) {
			throw new MyException("已交卷");
		}
		if (myExam.getState() == 2) {
			throw new MyException("考试进行中");
		}
		// 标记为考试中
		myExam.setState(2);
		myExam.setAnswerStartTime(new Date());
		myExam.setUpdateTime(new Date());
		myExam.setUpdateUserId(userId);
		update(myExam);

		new Thread(() -> opLogService.log(userId, LogType.MN.getValue())).start();
	}

	@Override
	public void publish(ExamMockInfo examInfo, String processBarId) {
		/**
		 * 数据处理
		 * 1：如果是人工组卷，且从题库抽题，试题类型等重新查一遍数据库，不要依赖前端，用于检测包含主观题等
		 * 2：如果是随机组卷，提前返回需要的题库，用于随机抽题
		 */
//		examInfo.setName("模拟考试");
//		Double processLen = (examInfo.getExamUserIds().length + 5) * 1.0;// 说明参考控制层
		Double processLen = 5.0;
		ProgressBarCache.setProgressBar(processBarId, 0.0, processLen, "校验前数据处理开始", HttpStatus.OK.value());
		Map<Integer, List<Question>> questionListCache = publishHandle(examInfo);
		ProgressBarCache.setProgressBar(processBarId, 1.0, processLen, "校验前数据处理完成", HttpStatus.OK.value());

		// 数据校验
		publishValid(examInfo, questionListCache);
		ProgressBarCache.setProgressBar(processBarId, 2.0, processLen, "校验数据完成", HttpStatus.OK.value());

		// 保存考试信息（直接分配试卷到考试用户）
//		MyExamMock exam = publishExam(examInfo);
		ProgressBarCache.setProgressBar(processBarId, 3.0, processLen, "生成考试完成", HttpStatus.OK.value());

		// 保存试卷信息 （不需要保存试题规则，直接分配试卷到考试用户）
//		publishPaper(examInfo);
		ProgressBarCache.setProgressBar(processBarId, 4.0, processLen, "生成试卷完成", HttpStatus.OK.value());

		// 分配试卷到考试用户
		publishUser(examInfo, questionListCache, processBarId);

		// 标记为需要监听的考试（考试结束自动阅卷）*****************************************************
//		AutoMarkCache.put(exam.getId(), exam);
//		log.info("考试核心加入：【{}-{}】加入监听，{}开始自动阅卷",
//				exam.getId(),
//				exam.getName(), // 未阅卷 取 考试结束时间；阅卷中 取 阅卷结束时间
//				exam.getMarkState() == 1 ? DateUtil.formatDateTime(exam.getEndTime()) : DateUtil.formatDateTime(exam.getMarkEndTime()));
	}

	private MyExamMock publishExam(ExamMockInfo examInfo) {
		MyExamMock exam = new MyExamMock();
		if (ValidateUtil.isValid(examInfo.getId())) {
			exam = getEntity(examInfo.getId());
		}
		exam.setName(examInfo.getName());
		exam.setPaperName(examInfo.getPaperName());
		exam.setUserId(examInfo.getUserId());
//		exam.setStartTime(examInfo.getStartTime());
//		exam.setEndTime(examInfo.getEndTime());
		exam.setScoreState(examInfo.getScoreState());
		exam.setPassTime(examInfo.getPassTime());
		exam.setPassScore(examInfo.getPassScore());
		exam.setTotalScore(examInfo.getTotalScore());
		exam.setGenType(2);//组卷随机
		exam.setSxes(examInfo.getSxes());
		exam.setState(examInfo.getState());
		exam.setMarkState(3);// 标记为已阅卷
		exam.setUpdateUserId(getCurUser().getId());
		exam.setUpdateTime(new Date());

		if (!ValidateUtil.isValid(examInfo.getId())) {
			exam.setCreateUserId(getCurUser().getId());
			add(exam);
			examInfo.setId(exam.getId());// 同步exam.id到examInfo.id
		} else {
			update(exam);
		}
		return exam;
	}

	private void publishUser(ExamMockInfo examInfo, Map<Integer, List<Question>> questionListCache, String processBarId) {
		// 删除用户试卷、协助阅卷用户
//		List<MyExam> myExamList = myExamService.getList(examInfo.getId());// 100道题100个用户需要删除1万多次，改成sql删除
//		for (MyExam myExam : myExamList) {
//			List<MyQuestion> myQuestionList = myMockQuestionService.getList(myExam.getExamId(), myExam.getUserId());
//			for (MyQuestion myQuestion : myQuestionList) {
//				myMockQuestionService.del(myQuestion.getId());
//			}
//
//			myExamService.del(myExam.getId());
//		}
		/**
		 * 暂时删除，都是重新创建试卷和试题
		 */
//		myExamMockService.clear(examInfo.getId());
//		myMockQuestionService.clear(examInfo.getId());

//		List<MyMark> myMarkList = myMarkService.getList(examInfo.getId());
//		for (MyMark myMark : myMarkList) {
//			myMarkService.del(myMark.getId());
//		}

		// 重新生成用户试卷
//		Map<Integer, List<QuestionOption>> questionOptionCache = new HashMap<>();
//		Map<Integer, List<QuestionAnswer>> questionAnswerCache = new HashMap<>();
		int curProgressNum = 1;// 当前保存进度
//		Double processLen = (examInfo.getExamUserIds().length + 5) * 1.0;// 说明参考控制层
		Double processLen = 5.0;
		//不需要循环，只生成当前用户的考试题
//		for (Integer examUserId : examInfo.getExamUserIds()) {
			MyExamMock myExam = new MyExamMock();// 生成我的考试信息
//			myExam.setExamId(examInfo.getId());
			myExam.setUserId(getCurUser().getId());
		myExam.setGenType(2);//组卷随机
		myExam.setSxes(examInfo.getSxes());
			//myExam.setMarkUserId(1); //由管理员、子管理员或阅卷用户自己领取自己分配
			myExam.setState(1);// 未考试
			myExam.setMarkState(1);// 未阅卷
			myExam.setScoreState(3);//交卷后
			myExam.setUpdateTime(new Date());
			myExam.setUpdateUserId(getCurUser().getId());
			myExam.setPassTime(examInfo.getPassTime());
//			myExam.setTotalScore(examInfo.getTotalScore());
			myExam.setExamTotalScore(examInfo.getTotalScore());
			myExam.setPassScore(examInfo.getPassScore());
			myExam.setName("模拟考试");
			myExam.setPaperName(examInfo.getName());
			add(myExam);
			examInfo.setId(myExam.getId());// 同步exam.id到examInfo.id

			if (examInfo.getGenType() == 2) {// 如果是随机组卷，按抽题规则生成我的试卷（校验里判断过规则是否满足，不用在判断）
				Set<Question> questionOfUsed = new HashSet<>();
				int no = 1;
				for (int i = 0; i < examInfo.getExamRules().length; i++) {
					ExamMockRuleBean examRule = examInfo.getExamRules()[i];
					if (examRule.getType() == 1) {// 如果是章节
						MyMockQuestion myQuestion = new MyMockQuestion();
						myQuestion.setType(examRule.getType());
						myQuestion.setChapterName(examRule.getChapterName());
						myQuestion.setChapterTxt(examRule.getChapterTxt());
						myQuestion.setUserId(getCurUser().getId());
						myQuestion.setMockExamId(examInfo.getId());
						myQuestion.setNo(no++);
						myMockQuestionService.add(myQuestion);
					} else
						{// 如果是规则
						List<Question> questionList = questionListCache.get(examRule.getQuestionTypeId());
						Collections.shuffle(questionList);// 从当前规则中随机抽题（乱序模拟随机）
						Integer ruleRemainNum = examRule.getNum();// 该规则试题数量，找到一个数量减一
						for(Question question : questionList) {
							if (ruleRemainNum <= 0) {// 满足规则，处理下一个规则
								break;
							}
							if (questionOfUsed.contains(question)) {// 已经使用过的试题就不能在用，继续找下一个
								continue;
							}
							if (examRule.getQuestionType() != question.getType() // 当前试题不符合当前抽题规则，继续找下一个
									) {
								continue;
							}

							MyMockQuestion myQuestion = new MyMockQuestion();
							myQuestion.setType(examRule.getType());
							myQuestion.setScore(examRule.getScore());
//							myQuestion.setMarkOptions(examRule.getMarkOptions());
							myQuestion.setQuestionId(question.getId());
							myQuestion.setUserId(getCurUser().getId());
							myQuestion.setMockExamId(examInfo.getId());
							myQuestion.setNo(no++); // 试题乱序无效，因为本身就是随机的

							if (QuestionUtil.hasMultipleChoice(question)) {// 如果是多选，使用抽题规则的漏选分数
								myQuestion.setScores(examRule.getScores());
							}
//							else if ((QuestionUtil.hasFillBlank(question) || QuestionUtil.hasQA(question)) // 如果是客观填空问答，把分数平均分配到子分数
//									&& QuestionUtil.hasObjective(question)) {// 如果抽题不设置分数，使用题库默认的分数，会导致总分不确定
//								if (questionAnswerCache.get(myQuestion.getQuestionId()) == null) {// 如果抽题设置分数，主观题答案数量不一样，没法按答案分配分数
//									questionAnswerCache.put(myQuestion.getQuestionId(), questionAnswerService.getList(myQuestion.getQuestionId()));
//								}
//								List<QuestionAnswer> questionAnswerList = questionAnswerCache.get(myQuestion.getQuestionId());// 所以规则为当题分数，平均分配到每个答案
//								myQuestion.setScores(splitScore(examRule.getScore(), questionAnswerList.size()));
//							}

							myQuestion.setUpdateTime(new Date());
							myQuestion.setUpdateUserId(getCurUser().getId());
							myMockQuestionService.add(myQuestion);

							questionOfUsed.add(question);
							ruleRemainNum--;
						}
					}
				}
			}

			ProgressBarCache.setProgressBar(processBarId, 4.0 + curProgressNum++, processLen, "生成用户-"+getCurUser().getId()+"试卷完成", HttpStatus.OK.value());
//		}

//		for (Integer markUserId : examInfo.getMarkUserIds()) {
//			MyMark myMark = new MyMark();
//			myMark.setExamId(examInfo.getId());
//			myMark.setMarkUserId(markUserId);
//			myMark.setQuestionIds(null);
//			myMark.setUpdateTime(new Date());
//			myMark.setUpdateTime(new Date());
//			myMarkService.add(myMark);
//		}
	}

	private void publishValid(ExamMockInfo examInfo, Map<Integer, List<Question>> questionListCache) {
		// 校验考试信息
		publishValidExam(examInfo);
		// 校验试卷信息
		publishValidPaper(examInfo, questionListCache);
		// 校验考试用户信息
		publishValidUser(examInfo);
	}

	private void publishValidUser(ExamMockInfo examInfo) {
//		if (!ValidateUtil.isValid(examInfo.getExamUserIds())) {
//			throw new MyException("最少添加一个考试用户");
//		}
		if (!ValidateUtil.isValid(getCurUser().getId())) {
			throw new MyException("无考试用户");
		}
//		Set<Integer> examUserIdSet = new HashSet<>(Arrays.asList(examInfo.getExamUserIds()));
//		if (examUserIdSet.size() != examInfo.getExamUserIds().length) {
//			throw new MyException("考试用户重复");
//		}

//		if (getCurUser().getType() != 0) {
//			User curUser = userService.getEntity(getCurUser().getId());
//			Set<Integer> haveUsers = CollectionUtil.toSet(curUser.getUserIds());
//			Set<Integer> examUsers = CollectionUtil.toSet(examInfo.getExamUserIds());
//			if (!haveUsers.containsAll(examUsers)) {
//				throw new MyException("无用户操作权限");
//			}
//		}
//		if (getCurUser().getType() != 1) {
//			throw new MyException("无用户操作权限");
//		}

//		if (ValidateUtil.isValid(examInfo.getMarkUserIds())) {// 不一定有，如果有则校验是否重复
//			Set<Integer> markUserIdSet = new HashSet<>(Arrays.asList(examInfo.getMarkUserIds()));
//			if (markUserIdSet.size() != examInfo.getMarkUserIds().length) {
//				throw new MyException("阅卷用户重复");
//			}
//			if (examInfo.getMarkType() == 1) {
//				throw new MyException("自动阅卷，无需子管理员参与");// 需要人工阅卷也不一定需要子管理员，admin就可以
//			}
//		}
	}

	private void publishValidPaper(ExamMockInfo examInfo, Map<Integer, List<Question>> questionListCache) {
		if (examInfo.getGenType() == 2) {
			if (!ValidateUtil.isValid(examInfo.getExamRules())) {
				throw new MyException("最少添加一条规则");
			}

			Set<Question> questionOfUsed = new HashSet<>();// 已使用过的试题
			int ruleNo = 0; // 用于过滤章节后，题数不足时，提示那个规则无效
			for (int i = 0; i < examInfo.getExamRules().length; i++) {
				ExamMockRuleBean examRule = examInfo.getExamRules()[i];
				if (!ValidateUtil.isValid(examRule.getType())
						|| (examRule.getType() != 1 && examRule.getType() != 2)) {
					throw new MyException("参数错误：examRule.type");
				}
				if (examRule.getType() == 1) {// 如果是章节
					//if (!ValidateUtil.isValid(examRule.getChapterName())) { // 没有也可以
					//	throw new MyException("参数错误：examRule.chapterName");
					//}
					if (ValidateUtil.isValid(examRule.getQuestionTypeId())) {
						throw new MyException("参数错误：examRules.questionTypeId");
					}
					if (ValidateUtil.isValid(examRule.getQuestionType())) {
						throw new MyException("参数错误：examRules.questionType");
					}
//					if (ValidateUtil.isValid(examRule.getMarkType())) {
//						throw new MyException("参数错误：examRules.markType");
//					}
//					if (ValidateUtil.isValid(examRule.getMarkOptions())) {
//						throw new MyException("参数错误：examRules.markOptions");
//					}
					if (ValidateUtil.isValid(examRule.getNum())) {
						throw new MyException("参数错误：examRules.num");
					}
					if (ValidateUtil.isValid(examRule.getScore())) {
						throw new MyException("参数错误：examRules.score");
					}
					if (ValidateUtil.isValid(examRule.getScores())) {
						throw new MyException("参数错误：examRules.scores");
					}
				} else if (examRule.getType() == 2) {// 如果是规则
					if (ValidateUtil.isValid(examRule.getChapterName())) {
						throw new MyException("参数错误：examQuestions.chapterName");
					}
					if (ValidateUtil.isValid(examRule.getChapterTxt())) {
						throw new MyException("参数错误：examQuestions.chapterTxt");
					}


					if (!ValidateUtil.isValid(examRule.getQuestionTypeId())) {
						throw new MyException("参数错误：examRules.questionTypeId");
					}
					if (!ValidateUtil.isValid(examRule.getQuestionType())
							|| examRule.getQuestionType() < 1 || examRule.getQuestionType() > 5) {
						throw new MyException("参数错误：examRules.questionType");
					}
//					if (!ValidateUtil.isValid(examRule.getMarkType())
//							|| (examRule.getMarkType() != 1 && examRule.getMarkType() != 2)) {
//						throw new MyException("参数错误：examRules.markType");
//					}
					if (!ValidateUtil.isValid(examRule.getNum())) {
						throw new MyException("参数错误：examRules.num");
					}
					if (examRule.getNum() <= 0 || examRule.getNum() > 100) {
						throw new MyException("参数错误：examRules.num");
					}

					if (!ValidateUtil.isValid(examRule.getScore())) {
						throw new MyException("参数错误：examRules.score");
					}
					if (examRule.getScore().doubleValue() <= 0 || examRule.getScore().doubleValue() > 20) {
						throw new MyException("参数错误：examRules.score");
					}

					if (examRule.getQuestionType() == 2) {// 如果是多选，必须有子分数（漏选分值）
						if (!ValidateUtil.isValid(examRule.getScores())) {
							throw new MyException("参数错误：examRules.scores");
						}
						if (examRule.getScores().length != 1) {
							throw new MyException("参数错误：examRules.scores");
						}
						if (examRule.getScore().doubleValue() <= examRule.getScores()[0].doubleValue()) {// 漏选分数不能大于分数
							throw new MyException("参数错误：examRules.scores");
						}
					}
					if (examRule.getQuestionType() != 2) {// 如果是单选判断不能有子分数，填空问答平均分配分数
						if (ValidateUtil.isValid(examRule.getScores())) {
							throw new MyException("参数错误：examRules.scores");
						}
					}

					ruleNo++;
					int validQuestionNum = 0;// 符合当前抽题规则的有效题数
					for (Question question : questionListCache.get(examRule.getQuestionTypeId())) {
//						if (!(CurLoginUserUtil.isSelf(question.getCreateUserId()) || CurLoginUserUtil.isAdmin())) {
//							throw new MyException(String.format("试题无权限，编号：%s", question.getId()));
//						}

						if (questionOfUsed.contains(question)) {// 已经使用过的试题就不能在用，继续找下一个
							continue;
						}
						if (validQuestionNum >= examRule.getNum()) {// 当前题库已满足当前抽题规则，不在继续验证
							break;
						}

						if (examRule.getQuestionType() == question.getType()) // 当前试题满足抽题规则
								 {
							questionOfUsed.add(question);// 加入已使用过的试题
							validQuestionNum++; // 有效题数加一
						}
					}

					if (validQuestionNum < examRule.getNum()) {
						throw new MyException(String.format("试卷配置：第【%s】个规则题数不足%s，请修改", ruleNo, examRule.getNum()));
					}
				}
			}
		}
	}

	private void publishValidExam(ExamMockInfo examInfo) {
//		if (ValidateUtil.isValid(examInfo.getId())) {// 如果是二次修改，校验考试是否结束
//			Exam exam = getEntity(examInfo.getId());
//			if (exam.getMarkType() == 3) {
//				throw new MyException("已阅卷");
//			}
//			if (exam.getEndTime().getTime() <= System.currentTimeMillis()) {
//				throw new MyException("考试已结束");
//			}
//			if (exam.getStartTime().getTime() <= System.currentTimeMillis()) {
//				throw new MyException("考试已开始");// 考试结束放在考试开始前校验，可能的问题为考试已结束，提示的是考试已开始。
//			}
//		}

		if (!ValidateUtil.isValid(examInfo.getName())) {
			throw new MyException("参数错误：name");
		}

		BigDecimal paperTotalScore = BigDecimal.ZERO;// 试卷总分
		BigDecimalUtil bigDecimalUtil = BigDecimalUtil.newInstance(0);
		boolean containSubjectiveQuesiton = false;// 包含主观试题
		{
//			if (examInfo.getGenType() == 1) {
//				if (!ValidateUtil.isValid(examInfo.getExamQuestions())) {
//					throw new MyException("最少添加一道试题");
//				}
//				for (ExamQuestionEx examQuestion : examInfo.getExamQuestions()) {
//					if (ExamUtil.hasQuestion(examQuestion)) {
//						bigDecimalUtil.add(examQuestion.getScore());// 单题分数从组卷后定制的分数中取
//						if (examQuestion.getMarkType() == 2) {// 简单校验markType就可以，保存试题时有详细校验
//							containSubjectiveQuesiton = true;
//						}
//					}
//				}
//			} else
			if (examInfo.getGenType() == 2) {
				if (!ValidateUtil.isValid(examInfo.getExamRules())) {
					throw new MyException("最少添加一条规则");
				}
				for (ExamMockRuleBean examRule : examInfo.getExamRules()) {
					if (examRule.getType()==2) {
						bigDecimalUtil.add(// 单题分数*数量累加到总分
								BigDecimalUtil.newInstance(examRule.getScore()).mul(examRule.getNum()).getResult());
//						if (examRule.getMarkType() == 2) {
//							containSubjectiveQuesiton = true;
//						}
					}
				}
			}
			paperTotalScore = bigDecimalUtil.getResult();
		}
		{
		}

		if (!ValidateUtil.isValid(examInfo.getScoreState())
				|| (examInfo.getScoreState() != 1 && examInfo.getScoreState() != 2 && examInfo.getScoreState() != 3)) {
			throw new MyException("参数错误：scoreState");
		}
		if (!ValidateUtil.isValid(examInfo.getTotalScore()) //总分无效
				|| examInfo.getTotalScore().doubleValue() <= 0 //总分小于0
				|| examInfo.getTotalScore().doubleValue() != paperTotalScore.doubleValue()) {//总分和卷面分数不相等
			throw new MyException("参数错误：totalScore");
		}

		if (!ValidateUtil.isValid(examInfo.getPassScore()) // 及格分数无效
				|| examInfo.getPassScore().doubleValue() < 0 // 小于0
				|| examInfo.getPassScore().doubleValue() > examInfo.getTotalScore().doubleValue()) {// 大于总分
			throw new MyException("参数错误：passScore");
		}
	}

	private Map<Integer, List<Question>> publishHandle(ExamMockInfo examInfo) {
//		if (examInfo.getGenType() == 1) {// 如果是人工组卷
//			for (ExamQuestionEx examQuestion : examInfo.getExamQuestions()) {
//				if (ExamUtil.hasQuestion(examQuestion)) {// 如果是试题
//					if (ValidateUtil.isValid(examQuestion.getQuestionId())) {// 如果是从题库抽的题
//						Question question = QuestionCache.getQuestion(examQuestion.getQuestionId());
//						examQuestion.setQuestionType(question.getType());// 试题类型等重新查一遍数据库，不要依赖前端
//						examQuestion.setMarkType(question.getMarkType());
//						// examQuestion.setTitle(question.getTitle());
//						// examQuestion.setAnalysis(question.getAnalysis());
//						//examQuestion.setOptions(null);// 用不着可以不查询
//						//examQuestion.setAnswers(null);//
//					}
//				}
//			}
//			return null;
//		}

		Map<Integer, List<Question>> questionListCache = new HashMap<>();
		if (examInfo.getGenType() == 2) {// 如果是随机组卷
			for (int i = 0; i < examInfo.getExamRules().length; i++) {
				ExamMockRuleBean examRule = examInfo.getExamRules()[i];
				if (!ValidateUtil.isValid(questionListCache.get(examRule.getQuestionTypeId()))) {
					List<Question> questionList = questionService.getList(examRule.getQuestionTypeId());
					questionListCache.put(examRule.getQuestionTypeId(), questionList);// 把题库缓存起来，用于模拟随机抽题
				}
			}
		}
		return questionListCache;
	}

	/**
	 * 拆分分数<br/>
	 * 1分 拆分成2份，结果：0.5、0.5<br/>
	 * 1分 拆分成3份，结果：0.33、0.33、0.34<br/>
	 *
	 * @param score
	 * @param num
	 * @return List<BigDecimal>
	 */
	private BigDecimal[] splitScore(BigDecimal score, int num) {
		BigDecimal[] scores = new BigDecimal[num];
		BigDecimal singleScore = BigDecimalUtil.newInstance(score).div(num, 2).getResult();
		for (int i = 0; i < num - 1; i++) {
			scores[i] = singleScore;
		}
		scores[num- 1] = BigDecimalUtil.newInstance(singleScore).mul(num - 1).sub(score).mul(-1).getResult();
		return scores;
	}

	private Integer[] shuffleNums(int start, int end) {
		Integer[] shuffleNums = new Integer[end - start + 1];
		for (int i = 0; i < shuffleNums.length; i++) {
			shuffleNums[i] = start + i;
		}

		ArrayUtils.shuffle(shuffleNums);
		return shuffleNums;
	}

	@Override
	public MyExamMock examUnStart(Integer userId) {
		return myExamMockDao.examUnStart(userId);
	}

	/**
	 * 是否存在正在进行的模拟考试
	 *
	 * @param userId
	 * @return
	 */
	@Override
	public PageResult hasJxMockExam(Integer userId) {
		return myExamMockDao.hasJxMockExam(userId);
	}

	/**
	 * 超时未提交的模拟考试
	 */
	@Override
	public void endOverExam() {
		List<MyExamMock> listMustOver=myExamMockDao.getAllOverMyExam();
		if(listMustOver==null||listMustOver.isEmpty()){
			log.debug("没有(已经考试超时的模拟考试)考试需要结算");
			return;
		}
		for (MyExamMock myExamMock : listMustOver) {
			doExamEx(myExamMock.getId(),false);
		}
	}

	/**
	 * 直接生成考试返回试题
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public Integer genPaper() {

		List<Map<String, Object>> myQuestionRule = myExamMockDao.getMyQuestionFromRule(getCurUser().getId());
		if (!ValidateUtil.isValid(myQuestionRule)){
			throw new MyException("用户无题库");
		}
		List<Dict> lsDict = dictDao.getListByIndex("EXAM_MOCK_CONFIG");
		if (!ValidateUtil.isValid(lsDict)&&lsDict.size()<6){
			throw new MyException("模拟考试无配置信息");
		}
		List<ExamMockRuleQuestion> myQuestionRuleRadio = new ArrayList<>();
		List<ExamMockRuleQuestion> myQuestionRuleMul = new ArrayList<>();;
		List<ExamMockRuleQuestion> myQuestionRuleDet = new ArrayList<>();;
		for (Map<String, Object> map : myQuestionRule) {
			ExamMockRuleQuestion mockRuleQuestion = new ExamMockRuleQuestion();
			mockRuleQuestion.setQuestionId((Integer)map.get("id"));
			mockRuleQuestion.setType((Integer)map.get("type"));
			if((Integer)map.get("type")==1){
				myQuestionRuleRadio.add(mockRuleQuestion);
			}
			else if((Integer)map.get("type")==2){
				myQuestionRuleMul.add(mockRuleQuestion);
			}
			else if((Integer)map.get("type")==4){
				myQuestionRuleDet.add(mockRuleQuestion);
			}
		}
		// 重新生成用户试卷
		MyExamMock myExam = new MyExamMock();// 生成我的考试信息
		myExam.setUserId(getCurUser().getId());
		myExam.setPaperName("模拟考试"+DateUtil.formatDate(new Date()));
		myExam.setGenType(2);//组卷随机
		myExam.setState(1);// 未考试
		myExam.setMarkState(1);// 未阅卷
		myExam.setScoreState(3);//交卷后
		myExam.setUpdateTime(cn.hutool.core.date.DateUtil.date());
		myExam.setUpdateUserId(getCurUser().getId());
		myExam.setPassTime(Integer.parseInt(lsDict.get(0).getDictValue()));
		myExam.setExamTotalScore(new BigDecimal(Integer.parseInt(lsDict.get(1).getDictValue())));
		myExam.setPassScore(new BigDecimal(Integer.parseInt(lsDict.get(2).getDictValue())));
		myExam.setName("模拟考试");
		add(myExam);

		int no = 1;
		List<ExamMockRuleQuestion> lsR = selectRandomQuestion(myQuestionRuleRadio,Integer.parseInt(lsDict.get(3).getDictValue()));
		List<ExamMockRuleQuestion> lsM = selectRandomQuestion(myQuestionRuleMul,Integer.parseInt(lsDict.get(4).getDictValue()));
		List<ExamMockRuleQuestion> lsD = selectRandomQuestion(myQuestionRuleDet,Integer.parseInt(lsDict.get(5).getDictValue()));
		List<ExamMockRuleQuestion> lsRule = new ArrayList<>();
		lsRule.addAll(lsR);
		lsRule.addAll(lsM);
		lsRule.addAll(lsD);
		for (int i = 0; i < lsRule.size(); i++) {
			Question question = QuestionCache.getQuestion(lsRule.get(i).getQuestionId());
			MyMockQuestion myQuestion = new MyMockQuestion();
			myQuestion.setType(2);
			myQuestion.setScore(question.getScore());
//			myQuestion.setMarkOptions(examRule.getMarkOptions());
			myQuestion.setQuestionId(question.getId());
			myQuestion.setUserId(getCurUser().getId());
			myQuestion.setMockExamId(myExam.getId());
			myQuestion.setNo(no++); // 试题乱序无效，因为本身就是随机的

			if (QuestionUtil.hasMultipleChoice(question)) {// 如果是多选，使用抽题规则的漏选分数
				myQuestion.setScores(new BigDecimal[]{new BigDecimal(0)});
			}
			myQuestion.setUpdateTime(new Date());
			myQuestion.setUpdateUserId(getCurUser().getId());
			myMockQuestionService.add(myQuestion);
		}
		return myExam.getId();
	}

    public static List<ExamMockRuleQuestion> selectRandomQuestion(List<ExamMockRuleQuestion> lsq, int numToSelect) {
		if (lsq.size()<numToSelect){
			throw new MyException("题库数量不足");
		}
		Random random = new Random();
		//核心代码
		int[] indexes = random.ints(0, lsq.size()).distinct().limit(numToSelect).toArray();

		List<ExamMockRuleQuestion> selectedQuestion = new ArrayList<>();
		for (int i = 0; i < numToSelect; i++) {
			selectedQuestion.add(lsq.get(indexes[i]));
		}
		return selectedQuestion;
	}

	@Transactional
    @Override
    public void delExamMockById(Integer examId) {
		myExamMockService.clear(examId);
		myMockQuestionService.clear(examId);
    }
}
